package indi.fritz.bigmap.volme.data;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import indi.fritz.bigmap.common.DBException;
import indi.fritz.bigmap.util.ByteUtil;
import indi.fritz.bigmap.volme.model.DataOffset;
import indi.fritz.bigmap.volme.model.KeyValue;

public class DataEngine implements AutoCloseable{
	public static byte[] EOF = new byte[] {(byte)0XEF,(byte)0XCC,(byte)0XCC,(byte)0XCC,(byte)0XCC,(byte)0XCC,(byte)0XEF};
	
	File file;
	RandomAccessFile reader;
	
	public DataEngine(File file) throws IOException {
		this.file = file;
		if(!this.file.exists()) {
			throw new DBException.NotDataFile();
		}
		this.reader = new RandomAccessFile(file, "rw");
	}
	
	public DataOffset updateKeyValue(DataOffset offset, KeyValue kv) throws IOException {
		byte[] b = ByteUtil.merge(kv.getByte(), EOF);
		long start = 0;
		synchronized (this.reader) {
			try(DataEngineLock lock = getLock(offset.getStart(), offset.getLength(), false)){
				start = this.reader.length();
				this.reader.seek(start);
				this.reader.write(b);
				
				this.reader.seek(offset.getStart() + KeyValue.existIndex());
				this.reader.write(ByteUtil.booleanToByte(false));
				
				offset.setStart(start);
				offset.setExist(kv.isExist());
				offset.setLength(b.length);
			}
		}
		return offset;
	}
	
	public DataOffset writeKeyValue(KeyValue kv)throws IOException {
		byte[] b = ByteUtil.merge(kv.getByte(), EOF);
		long start = 0;
		synchronized (this.reader) {
			try(DataEngineLock lock = getLock(start, b.length, false)){
				start = this.reader.length();
				this.reader.seek(start);
				this.reader.write(b);
			}
		}
		return new DataOffset(start, b.length, kv.isExist());
	}
	
	public KeyValue readKeyValue(DataOffset data) throws IOException {
		if(!data.isExist()){ return null; }
		synchronized (this.reader) {
			try(DataEngineLock lock = getLock(data.getStart(), data.getLength(), true)){
				this.reader.seek(data.getStart());
				byte[] b = new byte[data.getLength()];
				this.reader.read(b, 0, b.length);
				return KeyValue.format(b);
			}
		}
	}
	
	public KeyValue delete(DataOffset data) throws IOException {
		synchronized (this.reader) {
			try(DataEngineLock lock = getLock(data.getStart(), data.getLength(), false)){
				this.reader.seek(data.getStart());
				byte[] b = new byte[data.getLength()];
				this.reader.read(b, 0, b.length);
				KeyValue kv = KeyValue.format(b);
				this.reader.seek(data.getStart() + KeyValue.existIndex());
				this.reader.write(ByteUtil.booleanToByte(false));
				kv.setExist(false);
				return kv;
			}
		}
	}
	
	public void clear() throws IOException {
		synchronized (this.reader) {
			try(DataEngineLock lock = getLock(0L, Long.MAX_VALUE, false)){
				this.reader.setLength(0);
			}
		}
	}
	
	private DataEngineLock getLock(long position,long size,boolean shared) throws IOException {
		return new DataEngineLock(this.reader, position, size, shared);
	}
	
	@Override
	public void close() throws IOException {
		if(this.reader != null) {
			this.reader.close();
		}
		this.reader = null;
	}
	
	
}
